home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / Documentation / Tech Notes & Articles / Recipes / Storage / RefCounting < prev    next >
Encoding:
Text File  |  1995-07-11  |  8.5 KB  |  212 lines  |  [TEXT/ttxt]

  1. OpenDoc™ Recipes
  2.  
  3.  
  4. Reference Counting
  5. By The OpenDoc Design Team
  6. July 11, 1995.
  7.  
  8.  
  9. © 1993-1995  Apple Computer, Inc. All Rights Reserved.
  10. Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
  11. Mac and OpenDoc are trademarks of Apple Computer, Inc.
  12.  
  13. Changes since DR2
  14.  
  15. 1) Refcounting methods name change.
  16.  
  17.  
  18.  
  19. Why do we need ODRefCntObject?
  20.  
  21. During an OpenDoc session, many objects are created. Since there is a very complex relationshiop among objects, it is difficult to determine when it is safe to delete an object.
  22.  
  23. Ref-counting is a way to determine when these runtime objects can be deleted so that valuable memory space can be reclaimed.
  24.  
  25. What is ODRefCntObject?
  26.  
  27. ODRefCntObject is an object with a refCount. A refCount must be 0 or a positive
  28. integer.
  29.  
  30. Here's the class definition of ODRefCntObject:
  31.  
  32.  
  33. interface ODRefCntObject :  ODObject
  34. {
  35.     void  InitRefCntObject();
  36.     void  Acquire();
  37.     void  Release();
  38.     ODULong  GetRefCount();
  39. };
  40.  
  41.  
  42. What are the ODRefCntObjects in OpenDoc?
  43.  
  44. These are the subclasses of ODRefCntObject class:
  45.  
  46. ODLink                                                                                                (indiretly via ODPersistentObject)
  47. ODLinkSource                                    (indiretly via ODPersistentObject)
  48. ODPart                                                                                                (indiretly via ODPersistentObject)
  49. ODFrame                                                                                (indiretly via ODPersistentObject)
  50. ODContainer
  51. ODDocument
  52. ODDraft
  53. ODStorageUnit
  54. ODWindow
  55. ODExtension (and it's subclasses ODSemanticInterface, ODShellPlugIn, ODSettingsExtension)
  56. ODShape
  57. ODTransform
  58.  
  59. How does it work?
  60.  
  61. 1)    Every ODRefCntObject constructed has a RefCount of 1.
  62.  
  63. 2)    Every ODRefCntObject is "created" or "acquired" from an object which keeps 
  64.     track of it (a.k.a. factory object).
  65.         
  66.     ODRefCntObject                                                            Factory object
  67.     
  68.     ODLink                                                                                                                                        ODDraft
  69.     ODLinkSource                                            ODDraft
  70.     ODPart                                                                                                                                    ODDraft
  71.     ODFrame                                                                                        ODDraft
  72.     ODContainer                                                                                ODStorageSystem
  73.     ODDocument                                                            ODContainer
  74.     ODDraft                                                                                                                        ODDocument
  75.     ODStorageUnit                                                            ODDraft
  76.     ODWindow                                                                                             ODWindowState
  77. ODExtension                                                                                        ODObject (or subclass)
  78. ODShape                                                                                                                ODFrame
  79. ODTransform                                                                                ODFrame
  80.         
  81.     For example, to get a ODFrame object, one has to call ODDraft's CreateFrame or
  82.     ODDraft's AcquireFrame. The ODFrame object returned from ODDraft's CreateFrame will     have a RefCount of 1 while the ODFrame object returned from ODDraft's AcquireFrame will     have a RefCount of at least 1.
  83.     
  84.     Let's look at a very simple example:
  85.     
  86. ODFrame*    frame1 = draft->CreateFrame(…);                                // frame1's RefCount is 1
  87. ODFrame*    frame2 = draft->AcquireFrame(…);                                            // frame2 == frame1 and                                                            
  88.                                                                                                                                                                                                 // its RefCount is 2
  89.  
  90. frame2->Release();                                                                                                                        // frame2's RefCount is 1.
  91.                                                                                                                                                                                                 // Since frame1 == frame2,                                                         
  92.                                                                                                                                                                                                 // frame1's RefCount is also 1.
  93.  
  94. // The user should not use frame2 after this point!
  95.  
  96. frame1->Acquire(…);                                                                                                                    // frame1's RefCount is 2
  97.  
  98. frame1->Release(…);                                                                                                                    // frame1's RefCount is 1
  99.  
  100. frame1->Release(…);                                                                                                                    // frame1's RefCount is 0
  101.  
  102. //     The user should not use frame1 after this point!
  103.  
  104. frame1->Acquire(…); or                                                                                                     // ERROR!!!!!!!
  105.  
  106. frame1->Release(…);                                                                                                                    // ERROR!!!!!!!
  107.         
  108. 3)    When the RefCount goes down to 0, it is the object's responsibility to tell the
  109.     factory object about it.
  110.     
  111.     For example, ODFrame's Release() can be implemented like this:
  112.     
  113. void ODFrameRelease(ODFrame* somSelf, Environment* ev)
  114. {
  115.     parent_Release(somSelf, ev); // which calls ODRefCntObject's Release()
  116.  
  117.     if (somSelf->GetRefCount(ev) == 0)
  118.         fDraft->ReleaseFrame(ev, somSelf);    // Call the factory object
  119. }
  120.         
  121.     The factory object can choose to dispose of the object immediately or keep the object around until a purge is called upon itself.
  122.  
  123.     In the OpenDoc Macintosh implementation, ODDraft uses the latter strategy. Therefore, ODDraft will not delete the object as soon as ODDraft's ReleaseXXX is called. Instead,     it will put this released object in a collection so that it can be retrieved and reused again (i.e., when ODDraft's AcquireXXX is called for that object). But if ODDraft's Purge() is     called, all the released objects in the collection will be deleted.
  124.  
  125. The reason why the object is not deleted immediately is that the object may be reused in the near future. One good example is scrolling. Parts may choose to release objects that are scrolled out of view and re-get them when they come into view again. If we delete the objects as soon as ReleaseXXX is called, we will have to reinstantiate these objects again. Creating persistent objects is not a fast process as internalization will require accessing the secondary storage (namely, the disk).
  126.  
  127. 4)    Every time a reference to a ODRefCntObject (i.e., ODRefCntObject pointer) is  cached in some data structure, ODRefCntObj->Acquire() should be called by the code that manipulates this data structure. When the code is finished with the ODRefCntObj reference in the data structure, it should call ODRefCntObj->Release(). 
  128.     
  129.     For example, since ODFrame is keeping a reference to the containing frame and the part,     it has to increment the refCounts on both of these objects.
  130.  
  131. void ODFrameInitFrame(ODFrame*    somSelf,
  132.                             Environment* ev,
  133.                             ODStorageUnit*    storageUnit,
  134.                             ODFrame*         containingFrame,
  135.                             ODShape*         frameShape,
  136.                             ODPart*             part,
  137.                             ODTypeToken        viewType,
  138.                             ODTypeToken        presentation,
  139.                             ODULong             frameGroup,
  140.                             ODBoolean        isRoot,
  141.                             ODBoolean         isOverlaid)
  142. {
  143.     ......
  144.     _fContainingFrame = containingFrame;
  145.     if (_fContainingFrame != kODNULL)
  146.         _fContainingFrame->Acquire(ev);
  147.     _fPart = part;
  148.     if (_fPart != kODNULL)
  149.         _fPart->Acquire(ev);
  150.     ......
  151. }
  152.  
  153.     In ODFrame's ReleaseAll(), the frame has to release the containing frame and the part. Otherwise, the containing frame and the part will always have a RefCount greater than 0 and they will never be disposed of.
  154.     
  155. void ODFrameReleaseAll(ODFrame* somSelf, Environment* ev)
  156. {
  157.  
  158.     if (_fContainingFrame != kODNULL)
  159.         _fContainingFrame->Release(ev);
  160.  
  161.     if (_fPart != kODNULL)
  162.         _fPart->Release(ev);
  163.  
  164. }
  165.  
  166.     Note that the containing frame and the part may have RefCounts greater than 0 even after ODFrame's ReleaseAll() is called. This is because other objects may be keeping a reference to the containing frame or the part. 
  167.  
  168. What are the implications on ODPersistentObjects?
  169.  
  170. When the RefCount of a persistent object goes from 1 to 0, the part has to call its
  171. factory object (i.e. ODDraft). The code looks something like this:
  172.     
  173. void MyPartRelease(MyPart* somSelf, Environment* ev)
  174. {
  175.  
  176.     parent_Release(ev); // which calls ODRefCntObject's Release(ev).
  177.     if (somSelf->GetRefCount(ev) == 0)
  178.         _fDraft->ReleasePart(ev, somSelf);
  179.  
  180. }
  181.  
  182. As mentioned above, ODDraft does not delete the released object immediately. Therefore,  a part does not need to do any shutting down or deallocation when its RefCount goes down to 0. They will then be done in  in ReleaseAll() and the destructor (i.e., somUninit).
  183.  
  184. One should not consider a persistent object with a zero refcount a dead object because at any point the draft may increment its refcount and hand it to some other objects. Having a refcount of 0  simply means no one has a reference to the object.
  185.  
  186. However, if a part needs to function differently when there is no reference to it, it can use the RefCount to identify the situation and respond appropriately. For example, it may also choose to get rid of any structure or service that is not needed anymore. For example, a Communication part  may choose to close its driver when there is no reference to it.
  187.  
  188. void CommPartRelease(CommPart* somSelf, Environment* ev)
  189. {
  190.     parent_Release(ev);
  191.  
  192.     if (somSelf->GetRefCount(ev) == 0) {
  193.  
  194.         CloseDriver();
  195.         fDraft->ReleasePart(ev, somSelf);
  196.  
  197.     }
  198. }
  199.  
  200. If the object does something other than calling its factory object when its RefCount goes down to 0,  it needs to "reinitialize" itself when its RefCount is bumped from 0 to 1. This is being done in the object's Acquire() call. In general, the object should undo what it did in Release() when the RefCount went
  201. down to 0.
  202.  
  203. The following is an example of what the above-mentioned CommPart would do in response to its RefCount going from 0 to 1.
  204.  
  205. void CommPartAcquire(CommPart* somSelf, Environment* ev)
  206. {
  207.     parent_Acquire(somSelf, ev);
  208.     if (somSelf->GetRefCount(ev) == 1) {
  209.         OpenDriver();
  210.     }
  211. }
  212.